package ChatLogic;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

public class ReceiveFile {

    private ArrayList<Socket> sockets = new ArrayList<>();
    private ArrayList<byte[]> tempFrames = new ArrayList<>();
    private Socket communicationSocket;
    private Long frameSize = new Long(0);
    private Long fileSize = new Long(0);
    long lastFrameWritten = -1;
    private boolean finalFrameWritten = false;
    String fileName = "";
    FileOutputStream fos;
    int totalNrOfFrames = 0;
    static long startTime = -1;

    public synchronized void writeToFile(byte[] frame, long frameNr) {

        
        if (frame == null) {
            return;
        }
        try {
            if (frameNr == lastFrameWritten + 1) {

                if (!(frameNr + 1 == totalNrOfFrames)) {
                    System.out.println("Skreiv frame" + frameNr + "(" + frame.length + ")" + " til disk" + "    (" + (frameSize * (frameNr + 1)) + "/" + fileSize + ")");
                } else {
                    finalFrameWritten("yes");
                    System.out.println("Skreiv frame" + frameNr + "(" + frame.length + ")" + " til disk" + "    (" + (frameSize * (frameNr)+frame.length) + "/" + fileSize + ")");
                    System.out.println("Det tok " + (System.currentTimeMillis() - startTime)/1000 + "sek å sende "+fileSize+"bytes ("+new Double((fileSize/((System.currentTimeMillis() - startTime)))/1000)+" MB/s)");
                    new DataOutputStream(communicationSocket.getOutputStream()).writeUTF("done");
                }

                fos.write(frame);
                lastFrameWritten = frameNr;
                if (tempFrames.size() > 0 && tempFrames.get(0) != null) {
                    writeToFile(tempFrames.remove(0), frameNr + 1);
                } else if (tempFrames.size() > 0) {
                    tempFrames.remove(0);
                };
            } else {
                while (tempFrames.size() < (int) ((frameNr - lastFrameWritten - 1))) {
                    tempFrames.add(null);
                }
                tempFrames.set((int) ((frameNr - lastFrameWritten - 2)), frame);
                System.out.println("La til frame" + frameNr + "(" + frame.length + ")" + " på bufferplass" + (frameNr - lastFrameWritten - 2) + ", venter på frame" + (lastFrameWritten + 1));
            }
        } catch (IOException ex) {
            Logger.getLogger(ReceiveFile.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public synchronized boolean finalFrameWritten(String operation) {

        if (operation.equals("yes")) {
            finalFrameWritten = true;
            return true;
        }
        if (operation.equals("check")) {
            return finalFrameWritten;
        }
        return finalFrameWritten;
    }

    class ReceiveFrame implements Runnable {

        byte[] buffer = new byte[700000];
        Socket socket;
        int frameNr = -1;
        int readOfFrame = 0;
        int readFromStream = 0;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        public ReceiveFrame(Socket socket) {
            this.socket = socket;
        }

        @Override
        public void run() {

            try {
                socket.setReceiveBufferSize(10000);
                socket.setTcpNoDelay(true);

                DataInputStream dis = new DataInputStream(socket.getInputStream());
                while (true) {
                    frameNr = Integer.valueOf(dis.readUTF());

                    if (frameNr != totalNrOfFrames - 1) {
                        while (readOfFrame < frameSize) {
                            if ((frameSize - readOfFrame) < 65536) {
                                readFromStream = socket.getInputStream().read(buffer, 0, (int) (frameSize - readOfFrame));
                            } else {
                                readFromStream = socket.getInputStream().read(buffer);
                            }
                            readOfFrame += readFromStream;
                            baos.write(buffer, 0, readFromStream);
                            readFromStream = 0;
                        }
                    } else {
                        while (readOfFrame < fileSize - new Long(totalNrOfFrames - 1) * new Long(frameSize)) {
                            readFromStream = socket.getInputStream().read(buffer);
                            readOfFrame += readFromStream;
                            baos.write(buffer, 0, readFromStream);
                            readFromStream = 0;
                        }
                    }
                    writeToFile(baos.toByteArray(), frameNr);
                    baos.reset();
                    readOfFrame = 0;
                    frameNr = -1;
                }
            } catch (IOException ex) {
                if (!finalFrameWritten("check")) {
                    Logger.getLogger(ReceiveFile.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
    }

    public ReceiveFile(String location, String ip, int port, int nrOfSockets) {  //Igjen legge til socket i User kalt fileTransfer socket eller no
        //Må velge en location på et vis
        final int BUFFER_SIZE = 65536; //samme som tcp-pakke

        try {
            communicationSocket = new Socket(ip, port);
            new DataOutputStream(communicationSocket.getOutputStream()).writeUTF("receivecom");
            DataInputStream dis = new DataInputStream(communicationSocket.getInputStream());
            String fileInfo = "";

            while (fileInfo.equals("")) {
                fileInfo = dis.readUTF();
            }

            int counter = 0;
            for (int i = 0; i < fileInfo.length() - 3; i++) {
                if (fileInfo.substring(i, i + 3).equalsIgnoreCase("/*/")) {
                    if (counter == 0) {
                        fileName = fileInfo.substring(0, i);
                        fileInfo = fileInfo.substring(i + 3);
                    }
                    if (counter == 1) {
                        fileSize = Long.valueOf(fileInfo.substring(0, i));
                        fileInfo = fileInfo.substring(i + 3);
                    }
                    if (counter == 2) {
                        frameSize = Long.valueOf(fileInfo.substring(0, i));
                        break;
                    }
                    counter += 1;
                    i = 0;
                }
            }

            if (fileSize < frameSize) {
                totalNrOfFrames = 1;
            } else {
                Double d = Math.floor(fileSize / frameSize);

                totalNrOfFrames = d.intValue() + 1;
            }
            System.out.println(fileName);

            for (int i = 0; i < nrOfSockets; i++) {
                Socket socket=new Socket(ip, port);
                new DataOutputStream(socket.getOutputStream()).writeUTF("receivesocket");
                sockets.add(socket);
            }
            fos = new FileOutputStream(location + fileName);

           
            startTime = System.currentTimeMillis();
            for (int i = 0; i < nrOfSockets; i++) {
                new Thread(new ReceiveFrame(sockets.get(i))).start();
                System.out.println("kobler til socket " + (i + 1));
            }

            
        
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws Exception {
        new ReceiveFile("C:/Users/Stub/Desktop/Mottak/", "localhost", 10823, 4);//84.202.93.112
    }
}
